package org.artifact.core.plugin.thread;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.map.MapUtil;
import org.artifact.core.lang.IPlugin;

import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.artifact.core.lang.IServer;
import org.artifact.core.lang.MapPlus;

public class ThreadPlugin implements IPlugin {
	
	static final Log log = LogFactory.get(ThreadPlugin.class);
	
	private List<Map> threadConfigs = new ArrayList<>();
	
	private Map<String, ExecutorService> executorService = new HashMap<>();
	
	private int shutdownTimeOut = 180000;
	
	
	public ThreadPlugin() {
		Map config = IServer.me().getContext().getConfig();
		List<Map> threads = MapPlus.get("plugins.thread",config);
		threadConfigs.addAll(threads);
	}
	
	public ExecutorService getExecutorService(ThreadConfig config) {
		return executorService.get(config.getName());
	}
	
	public void execute(ThreadConfig config,Runnable command) {
		getExecutorService(config).execute(command);
	}

	@Override
	public boolean start() {
		try {
			for (Map threadConfig : threadConfigs) {
				ThreadFactory threadFactory = ThreadUtil.newNamedThreadFactory(MapUtil.getStr(threadConfig,"name"), false);
				ThreadPoolExecutor threadPoolExecutor = ExecutorBuilder.create().setCorePoolSize(MapUtil.getInt(threadConfig,"count")).setThreadFactory(threadFactory).build();
				executorService.put(MapUtil.getStr(threadConfig,"name"), threadPoolExecutor);
			}
		} catch (Exception e) {
			log.error(e);
		}
		return true;
	}

	@Override
	public boolean stop() {
		for (Entry<String, ExecutorService> e : executorService.entrySet()) {
			ExecutorService executorService = e.getValue();
			executorService.shutdown();
			try {
				if (!executorService.awaitTermination(getShutdownTimeOut(), TimeUnit.MILLISECONDS)) {
					log.error("-> {} shutdown TimeOut", e.getKey());
					executorService.shutdownNow();
				} else {
					log.info("-> {} shutdown TimeOut", e.getKey());
				}
			} catch (InterruptedException ie) {
				log.error("> {} shutdown TimeOut Error", e.getKey(), ie);
				executorService.shutdownNow();
				Thread.currentThread().interrupt();
			}
		}
		return true;
	}

	public int getShutdownTimeOut() {
		return shutdownTimeOut;
	}

	public void setShutdownTimeOut(int shutdownTimeOut) {
		this.shutdownTimeOut = shutdownTimeOut;
	}

	
}
